-module(redis).
-export([start/0, stop/0]).

-export([set/2, add/2, get/1, del/1, inc/1, dec/1, exist/1, rename/2]).
-export([set/3, add/3, get/2, del/2, inc/2, dec/2, exist/2, rename/3]).

-export([expire/2, expireat/2, persist/1]).
-export([expire/3, expireat/3, persist/2]).

-export([hSet/3, hGet/2, hMSet/2, hMGet/2, hInc/3, hDel/2, hGetAll/1, hExists/2]).
-export([hSet/4, hGet/3, hMSet/3, hMGet/3, hInc/4, hDel/3, hGetAll/2, hExists/3]).

-export([zAdd/3, zRem/2, zGet/1, zGet/3, zGetByScore/3, zGetByIndex/3, zGetEx/1, zGetEx/3, zGetExByScore/3]).
-export([zAdd/4, zRem/3, zGet/2, zGet/4, zGetByScore/4, zGetByIndex/4, zGetEx/2, zGetEx/4, zGetExByScore/4]).

-export([zGetV/1, zGetV/3, zGetVByScore/3, zGetVByIndex/3, zGetExV/1, zGetExV/3, zGetExVByScore/3, zSize/1]).
-export([zGetV/2, zGetV/4, zGetVByScore/4, zGetVByIndex/4, zGetExV/2, zGetExV/4, zGetExVByScore/4, zSize/2]).

-export([zScore/2, zIsMember/2, zRank/2, zRevRank/2, zInc/3, zUnion/2]).
-export([zScore/3, zIsMember/3, zRank/3, zRevRank/3, zInc/4, zUnion/3]).

-export([multi/0, exec/0, discard/0, watch/1, unwatch/0]).
-export([multi/1, exec/1, discard/1, watch/2, unwatch/1]).

-export([lPushR/2, lPushL/2, lTrim/3, lRange/3, lLen/1, lRem/3, lIndex/2]).
-export([lPushR/3, lPushL/3, lTrim/4, lRange/4, lLen/2, lRem/4, lIndex/3]).

-export([sAdd/2, sRem/2, sGetAll/1, sGetRandom/2, sSize/1, sIsMember/2]).
-export([sAdd/3, sRem/3, sGetAll/2, sGetRandom/3, sSize/2, sIsMember/3]).

-export([flushDB/0]).
-export([flushDB/1]).

start()->
	case application:start(redis_app) of
		ok -> ok;
		{error, {already_started, redis_app}} -> ok
	end.

stop() ->
    application:stop(redis_app),
    erlang:halt().

q(PoolName, Cmd) ->
	poolboy:transaction(PoolName, fun(RedisConn) -> eredis:q(RedisConn, Cmd) end).

%% Set Value
%% Set the string value of a key
set(Key, Value) ->
	set(redis_pool, Key, Value).
set(Pool, Key, Value) ->
	case q(Pool, ["SET", Key, Value]) of
		{ok, _} -> { ok, saved };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Add Key
%% Set the value of a key, only if the key does not exist
add(Key, Value) ->
	add(redis_pool, Key, Value).
add(Pool, Key, Value) ->
	case q(Pool, ["SETNX", Key, Value]) of
		{ok, <<"1">>} -> { ok, added };
		{ok, <<"0">>} -> { ok, exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get Value
%% Get the value of a key
get(Key) ->
	get(redis_pool, Key).
get(Pool, Key) ->
	case q(Pool, ["GET", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, undefined} -> { ok, undefined };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Delete Key
%% Delete a key
del(Key)  ->
	del(redis_pool, Key).
del(Pool, Key) ->
	case q(Pool, ["DEL", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, <<"1">>} -> { ok, deleted };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Inc Key 
%% Increment the integer value of a key by one
inc(Key) ->
	inc(redis_pool, Key).
inc(Pool, Key) ->
	case q(Pool, ["INCR", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, to_integer(Value) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Dec Key
%% Decrement the integer value of a key by one
dec(Key) ->
	dec(redis_pool, Key).
dec(Pool, Key) ->
	case q(Pool, ["DECR", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, to_integer(Value) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Check Key Exist
%% Determine if a key exists
exist(Key) ->
	exist(redis_pool, Key).
exist(Pool, Key) ->
	case q(Pool, ["EXISTS", Key]) of
		{ok, <<"1">>} -> { ok, exist };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Rename a key
rename(Key, NewKey) ->
	rename(redis_pool, Key, NewKey).
rename(Pool, Key, NewKey) ->
	case q(Pool, ["RENAME", Key, NewKey]) of
		{ok, _} -> { ok, renamed };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Set Key Expire TTL
%% Set a key's time to live in seconds
expire(Key, Seconds) ->
	expire(redis_pool, Key, Seconds).
expire(Pool, Key, Seconds) ->
	case q(Pool, ["EXPIRE", Key, Seconds]) of
		{ok, <<"1">>} -> { ok, set };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Set Key Expire At Time
%% Set the expiration for a key as a UNIX timestamp
expireat(Key, TimeStamp) ->
	expireat(redis_pool, Key, TimeStamp).
expireat(Pool, Key, TimeStamp) ->
	case q(Pool, ["EXPIREAT", Key, TimeStamp]) of
		{ok, <<"1">>} -> { ok, set };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Set Key Persist
%% Remove the expiration from a key
persist(Key) ->
	persist(redis_pool, Key).
persist(Pool, Key) ->
	case q(Pool, ["PERSIST", Key]) of
		{ok, <<"1">>} -> { ok, set };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Set Key 
%% Set the string value of a hash field
hSet(Key, Field, Value) ->
	hSet(redis_pool, Key, Field, Value).
hSet(Pool, Key, Field, Value) ->
	case q(Pool, ["HSET", Key, Field, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, <<"1">>} -> { ok, added };
		{ok, <<"0">>} -> { ok, saved };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Get Key 
%% Get the value of a hash field
hGet(Key, Field) ->
	hGet(redis_pool, Key, Field).
hGet(Pool, Key, Field) ->
	case q(Pool, ["HGET", Key, Field]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, undefined} -> { ok, undefined };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Set Multi Key
%% Set multiple hash fields to multiple values
hMSet(Key, FieldValueList) ->
	hMSet(redis_pool, Key, FieldValueList).
hMSet(Pool, Key, FieldValueList) ->
	case q(Pool, ["HMSET", Key | FieldValueList]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, _} -> { ok, saved };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Get Multi Key
%% Get the values of all the given hash fields
hMGet(Key, FieldList) ->
	hMGet(redis_pool, Key, FieldList).
hMGet(Pool, Key, FieldList) ->
	case q(Pool, ["HMGET", Key | FieldList]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, ValueList} -> { ok, ValueList };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Get All Values
hGetAll(Key) ->
	hGetAll(redis_pool, Key).
hGetAll(Pool, Key) ->
	case q(Pool, ["HGETALL", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, FieldValueList} -> { ok, FieldValueList };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Key Exists
%% Get the exists of a hash field
hExists(Key, Field) ->
	hExists(redis_pool, Key, Field).
hExists(Pool, Key, Field) ->
	case q(Pool, ["HEXISTS", Key, Field]) of
		{ok, <<"1">>} -> { ok, exist };
		{ok, <<"0">>} -> { ok, not_exist };
		{ok, <<"QUEUED">>} -> { ok, queued };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Hash Incr Key
%% Increment the integer value of a hash field by the given number
hInc(Key, Field, Inc) ->
	hInc(redis_pool, Key, Field, Inc).
hInc(Pool, Key, Field, Inc) ->
	case q(Pool, ["HINCRBY", Key, Field, Inc]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, to_integer(Value) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Delete one or more hash fields
%% Removes the specified fields from the hash stored at key
hDel(Key, FieldList) ->
	hDel(redis_pool, Key, FieldList).
hDel(Pool, Key, FieldList) ->
	case q(Pool, ["HDEL", Key | FieldList]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, _} -> { ok, deleted };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Set Add
%% Add one to a sorted set, or update its score if it already exists
zAdd(Key, Value, Score) ->
	zAdd(redis_pool, Key, Value, Score).
zAdd(Pool, Key, Value, Score) ->
	case q(Pool, ["ZADD", Key, Score, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, <<"1">>} -> { ok, added };
		{ok, <<"0">>} -> { ok, saved };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Set Remove
%% Remove one from a sorted set
zRem(Key, Value) ->
	zRem(redis_pool, Key, Value).
zRem(Pool, Key, Value) ->
	case q(Pool, ["ZREM", Key, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, <<"1">>} -> { ok, removed };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members
%% Sort Get All Member
zGet(Key) ->
	zGet(redis_pool, Key).
zGet(Pool, Key) ->
	case q(Pool, ["ZRANGEBYSCORE", Key, "-inf", "+inf"]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get All Member Limit by Offset and Count
zGet(Key, Offset, Count) ->
	zGet(redis_pool, Key, Offset, Count).
zGet(Pool, Key, Offset, Count) ->
	case q(Pool, ["ZRANGEBYSCORE", Key, "-inf", "+inf", "LIMIT", Offset, Count]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Member Score Between Min and Max
zGetByScore(Key, Min, Max) ->
	zGetByScore(redis_pool, Key, Min, Max).
zGetByScore(Pool, Key, Min, Max) ->
	case q(Pool, ["ZRANGEBYSCORE", Key, Min, Max]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Return a range of members in a sorted set, by index
zGetByIndex(Key, Offset, Count) ->
	zGetByIndex(redis_pool, Key, Offset, Count).
zGetByIndex(Pool, Key, Offset, Count) ->
	case q(Pool, ["ZRANGE", Key, Offset, Offset + Count - 1]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members With Score
%% Sort Get All Member
zGetEx(Key) ->
	zGetEx(redis_pool, Key).
zGetEx(Pool, Key) ->
	case q(Pool, ["ZRANGEBYSCORE", Key, "-inf", "+inf", "WITHSCORES"]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members With Score
%% Sort Get All Member Limit by Offset and Count
zGetEx(Key, Offset, Count) ->
	zGetEx(redis_pool, Key, Offset, Count).
zGetEx(Pool, Key, Offset, Count) ->
	case q(Pool, ["ZRANGEBYSCORE", Key, "-inf", "+inf", "WITHSCORES", "LIMIT", Offset, Count]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members With Score
%% Sort Get Member Score Between Min and Max
zGetExByScore(Key, Min, Max) ->
	zGetExByScore(redis_pool, Key, Min, Max).
zGetExByScore(Pool, Key, Min, Max) ->
	case q(Pool, ["ZRANGEBYSCORE", Key, Min, Max, "WITHSCORES"]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members
%% Sort Get All Member, with scores ordered from high to low
zGetV(Key) ->
	zGetV(redis_pool, Key).
zGetV(Pool, Key) ->
	case q(Pool, ["ZREVRANGEBYSCORE", Key, "+inf", "-inf"]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.
	
%% Sort Get Members
%% Sort Get All Member Limit by Offset and Count, with scores ordered from high to low
zGetV(Key, Offset, Count) ->
	zGetV(redis_pool, Key, Offset, Count).
zGetV(Pool, Key, Offset, Count) ->
	case q(Pool, ["ZREVRANGEBYSCORE", Key, "+inf", "-inf", "LIMIT", Offset, Count]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Member Score Between Min and Max
zGetVByScore(Key, Min, Max) ->
	zGetVByScore(redis_pool, Key, Min, Max).
zGetVByScore(Pool, Key, Min, Max) ->
	case q(Pool, ["ZREVRANGEBYSCORE", Key, Max, Min]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Return a range of members in a sorted set, by index, with scores ordered from high to low
zGetVByIndex(Key, Offset, Count) ->
	zGetVByIndex(redis_pool, Key, Offset, Count).
zGetVByIndex(Pool, Key, Offset, Count) ->
	case q(Pool, ["ZREVRANGE", Key, Offset, Offset + Count - 1]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members With Score
%% Sort Get All Member, with scores ordered from high to low
zGetExV(Key) ->
	zGetExV(redis_pool, Key).
zGetExV(Pool, Key) ->
	case q(Pool, ["ZREVRANGEBYSCORE", Key, "+inf", "-inf", "WITHSCORES"]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Members With Score
%% Sort Get All Member Limit by Offset and Count, with scores ordered from high to low
zGetExV(Key, Offset, Count) ->
	zGetExV(redis_pool, Key, Offset, Count).
zGetExV(Pool, Key, Offset, Count) ->
	case q(Pool, ["ZREVRANGEBYSCORE", Key, "+inf", "-inf", "WITHSCORES", "LIMIT", Offset, Count]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Sort Get Member Score Between Min and Max
zGetExVByScore(Key, Min, Max) ->
	zGetExVByScore(redis_pool, Key, Min, Max).
zGetExVByScore(Pool, Key, Min, Max) ->
	case q(Pool, ["ZREVRANGEBYSCORE", Key, Max, Min, "WITHSCORES"]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get Size 
%% Get the number of members in a sorted set
zSize(Key) ->
	zSize(redis_pool, Key).
zSize(Pool, Key) ->
	case q(Pool, ["ZCARD", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, to_integer(Value) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get Score
%% Get the score associated with the given member in a sorted set
zScore(Key, Value) ->
	zScore(redis_pool, Key, Value).
zScore(Pool, Key, Value) ->
	case q(Pool, ["ZSCORE", Key, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Result} -> { ok, to_integer(Result) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Has Key
%% Has Key In Sort Set
zIsMember(Key, Value) ->
	zIsMember(redis_pool, Key, Value).
zIsMember(Pool, Key, Value) ->
	case q(Pool, ["ZSCORE", Key, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, undefined} -> { ok, not_exist };
		{ok, _Result} -> { ok, exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.
	
	
%% Get Rank
%% Determine the index of a member in a sorted set
zRank(Key, Value) ->
	zRank(redis_pool, Key, Value).
zRank(Pool, Key, Value) ->
	case q(Pool, ["ZRANK", Key, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, undefined} -> { ok, not_exist };
		{ok, Result} -> { ok, to_integer(Result) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get Reverse Rank
%% Determine the index of a member in a sorted set, with scores ordered from high to low
zRevRank(Key, Value) ->
	zRevRank(redis_pool, Key, Value).
zRevRank(Pool, Key, Value) ->
	case q(Pool, ["ZREVRANK", Key, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, undefined} -> { ok, not_exist };
		{ok, Result} -> { ok, to_integer(Result) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Incr Score
%% Increment the score of a member in a sorted set
zInc(Key, Value, Increment) ->
	zInc(redis_pool, Key, Value, Increment).
zInc(Pool, Key, Value, Increment) ->
	case q(Pool, ["ZINCRBY", Key, Increment, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Result} -> { ok, to_integer(Result) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Union Sorted Set
zUnion(DestKey, SourceKeys) ->
	zUnion(redis_pool, DestKey, SourceKeys).
zUnion(Pool, DestKey, SourceKeys) ->
	case q(Pool, ["ZUNIONSTORE", DestKey, length(SourceKeys) | SourceKeys ]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Result} -> { ok, to_integer(Result) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Transaction Begin
%% Mark the start of a transaction block
multi() ->
	multi(redis_pool).
multi(Pool) ->
	case q(Pool, ["MULTI"]) of
		{ok, _} -> { ok, trans_begin };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Transaction Execute
%% Execute all commands issued after MULTI
exec() ->
	exec(redis_pool).
exec(Pool) ->
	case q(Pool, ["EXEC"]) of
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Transaction Cancel
%% Discard all commands issued after MULTI
discard() ->
	discard(redis_pool).
discard(Pool) ->
	case q(Pool, ["MULTI"]) of
		{ok, _} -> { ok, trans_canceled };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Watch
%% Watch the given keys to determine execution of the MULTI/EXEC block
watch(KeyList) ->
	watch(redis_pool, KeyList).
watch(Pool, KeyList) ->
	case q(Pool, ["WATCH" | KeyList]) of
		{ok, _} -> { ok, watched };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% UnWatch
%% Forget about all watched keys
unwatch() ->
	unwatch(redis_pool).
unwatch(Pool) ->
	case q(Pool, ["UNWATCH"]) of
		{ok, _} -> { ok, unwatched };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% List Push Tail
%% Append a value to a list, only if the list exists
lPushR(Key, ValueList) ->
	lPushR(redis_pool, Key, ValueList).
lPushR(Pool, Key, ValueList) ->
	case q(Pool, ["RPUSH", Key | ValueList]) of
		{ok, Count} -> { ok, to_integer(Count) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% List Push Head
%% Prepend one or multiple values to a list
lPushL(Key, ValueList) ->
	lPushL(redis_pool, Key, ValueList).
lPushL(Pool, Key, ValueList) ->
	case q(Pool, ["LPUSH", Key | ValueList]) of
		{ok, Count} -> { ok, to_integer(Count) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get List Values
%% Get a range of elements from a list
lRange(Key, Start, Stop) ->
	lRange(redis_pool, Key, Start, Stop).
lRange(Pool, Key, Start, Stop) ->
	case q(Pool, ["LRANGE", Key, Start, Stop]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Trim List
%% Trim a list to the specified range
lTrim(Key, Start, Stop) ->
	lTrim(redis_pool, Key, Start, Stop).
lTrim(Pool, Key, Start, Stop) ->
	case q(Pool, ["LTRIM", Key, Start, Stop]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, _} -> { ok, deleted };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get List' Length
%% Get the length of a list
lLen(Key) ->
	lLen(redis_pool, Key).
lLen(Pool, Key) ->
	case q(Pool, ["LLEN", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Count} -> { ok, to_integer(Count) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Remove elements from a list
%% Removes the first count occurrences of elements equal to value from the list stored at key.
%% The count argument influences the operation in the following ways:
    % count > 0: Remove elements equal to value moving from head to tail.
    % count < 0: Remove elements equal to value moving from tail to head.
    % count = 0: Remove all elements equal to value.
lRem(Key, Count, Value) ->
	lRem(redis_pool, Key, Count, Value).
lRem(Pool, Key, Count, Value) ->
	case q(Pool, ["LREM", Key, Count, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Removed} -> { ok, to_integer(Removed) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Returns the element at index index in the list stored at key.
%% The index is zero-based, so 0 means the first element, 1 the second element and so on.
%% Get an element from a list by its index
lIndex(Key, Index) ->
	lIndex(redis_pool, Key, Index).
lIndex(Pool, Key, Index) ->
	case q(Pool, ["LINDEX", Key, Index]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Value} -> { ok, Value };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Add Values to set
%% Add one or more members to a set
sAdd(Key, Values) ->
	sAdd(redis_pool, Key, Values).
sAdd(Pool, Key, Values) ->
	case q(Pool, ["SADD", Key | Values]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, AddedCount} -> { ok, to_integer(AddedCount) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Remove Values from a set
sRem(Key, Values) ->
	sRem(redis_pool, Key, Values).
sRem(Pool, Key, Values) ->
	case q(Pool, ["SREM", Key | Values]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, RemovedCount} -> { ok, to_integer(RemovedCount) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get All Values From a Set
sGetAll(Key) ->
	sGetAll(redis_pool, Key).
sGetAll(Pool, Key) ->
	case q(Pool, ["SMEMBERS", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Values} -> { ok, Values };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.
	
%% Get Random Values From a Set
sGetRandom(Key, Count) ->
	sGetRandom(redis_pool, Key, Count).
sGetRandom(Pool, Key, Count) ->
	% Redis v2.6
	case q(Pool, ["SRANDMEMBER", Key, Count]) of
	% Redis v2.4
	% case q(Pool, ["SRANDMEMBER", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, undefined} -> { ok, [] };
		% Redis v2.6
		{ok, Values} -> { ok, Values };
		% Resis v2.4
		% {ok, Values} -> { ok, [Values] };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Get Size of a Set
sSize(Key) ->
	sSize(redis_pool, Key).
sSize(Pool, Key) ->
	case q(Pool, ["SCARD", Key]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, Count} -> { ok, to_integer(Count) };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Is Member of a Set
sIsMember(Key, Value) ->
	sIsMember(redis_pool, Key, Value).
sIsMember(Pool, Key, Value) ->
	case q(Pool, ["SISMEMBER", Key, Value]) of
		{ok, <<"QUEUED">>} -> { ok, queued };
		{ok, <<"1">>} -> { ok, exist };
		{ok, <<"0">>} -> { ok, not_exist };
		{error, Reason} -> { error, { db_error, Reason } };
		_ -> { error, { db_error, unknown } }
	end.

%% Flush DB
flushDB() ->
	flushDB(redis_pool).
flushDB(Pool) ->
	q(Pool, ["FLUSHDB"]).

to_integer(true) -> 1;
to_integer(false) -> 0;
to_integer(undefined) -> 0;
to_integer(Val) when is_integer(Val) ->
	Val;
to_integer(Val) when is_list(Val)->
	list_to_integer(Val);
to_integer(Val) when is_binary(Val)->
	list_to_integer(binary_to_list(Val)).    